#include "Rtmp.h"
#include "rtmp/cmonitor.h"

#include <unistd.h>
#include "Debug.h"
#include <iostream>

using namespace std;

CRtmp::CRtmp(int iSocket): m_RtmpProtocol(iSocket)
{
    m_iSocket = iSocket;
    m_pRead = NULL;
    m_pWrite = NULL;

    m_bStreamSent = false;
}

CRtmp::~CRtmp()
{
    DBGMSG("rtmp destructor called");
    if(m_iSocket)
    {
        close(m_iSocket);
        m_iSocket = 0;
    }
}

void CRtmp::Init(fd_set *pRead, fd_set *pWrite)
{
    m_pRead = pRead;
    m_pWrite = pWrite;
    m_bStreamSent = false;
}

void CRtmp::SetFd()
{
    FD_SET(m_iSocket, m_pRead);
    if(/* something to send */ 0)
    {
        FD_SET(m_iSocket, m_pWrite);
    }
}

bool CRtmp::IsAlive()
{
    return m_RtmpProtocol.IsAlive();
}

bool CRtmp::Handshake()
{
    cout << "Try to handshake" << endl;
    m_RtmpProtocol.Handshake();
    cout << "Handshake done." << endl;
    return true;
}

bool CRtmp::GetConnectInfo(CConnectInfo &conInfo)
{
    m_RtmpProtocol.GetNextMessage(m_RtmpMsg);

    if (m_RtmpMsg.GetMsgType() != CRtmpMessage::AMF0_COMMAND_MESSAGE)
        return false;

    string _strInMessage = m_RtmpMsg.GetPayload();


    CAmf0StreamIn _amf0sin;
    _amf0sin.DecodeAmf(_strInMessage.data(),_strInMessage.size());
    CMonitor::Draw(_amf0sin);

    if (_amf0sin.At(0).toString() != "connect")
        return false;

    // auth name is sent int the third amf variable
    conInfo.SetAuthName(_amf0sin.At(3).toString());
    // display name is sent int the fifth amf variable
    conInfo.SetDisplayName(_amf0sin.At(5).toString());
    // password is sent int the fourth amf variable
    conInfo.SetPasswd(_amf0sin.At(4).toString());
    // sip URL is sent int the second amf variable. It is a object where first variable "app" carry sip URL
    conInfo.SetSipUrl(_amf0sin.At(2).toObject().at(0).second.toString());
    return true;
}

bool CRtmp::SetConnected()
{
    m_RtmpProtocol.SendMessage(rtmpMsgGenerator.ConnectReply());
    return true;
}

bool CRtmp::CheckInput()
{
    return m_RtmpProtocol.GetNextMessage(m_RtmpMsg);
}


bool CRtmp::GetDialInfo(CDialInfo &dinfo)
{
    // if message isn't AMF0 command message then return false
    if (m_RtmpMsg.GetMsgType() != CRtmpMessage::AMF0_COMMAND_MESSAGE)
        return false;

    string _strInMessage = m_RtmpMsg.GetPayload();
    CAmf0StreamIn _amf0sin;
    _amf0sin.DecodeAmf(_strInMessage.data(),_strInMessage.size());
    CMonitor::Draw(_amf0sin);


    dinfo.SetContactURL(_amf0sin.At(3).toString());
    cout << dinfo.GetContactURL() << endl;
    return true;
}

bool CRtmp::GetStream(std::string &strStream, int &iTimestamp)
{
    if (GetMessageCode() != CRtmp::RTMP_STREAM)
        return false;

    iTimestamp = m_RtmpMsg.GetTimestamp();
    strStream = m_RtmpMsg.GetPayload();

    return true;
}

bool CRtmp::SendStream(const std::string &strStream, int iTimestamp)
{
    m_RtmpProtocol.SendMessage(rtmpMsgGenerator.StreamMessage(strStream, 0), 5, m_bStreamSent?3:0);
    CRtmpMessage msg = rtmpMsgGenerator.StreamMessage(strStream, 0);

    m_bStreamSent = true;
    return true;
}

CRtmp::MsgType  CRtmp::GetMessageCode()
{


    CRtmpMessage::MsgType _RtmpMsgType = m_RtmpMsg.GetMsgType();

    // if incomming message is command
    if (_RtmpMsgType == CRtmpMessage::AMF0_COMMAND_MESSAGE)
    {
        string _strInMessage = m_RtmpMsg.GetPayload();
        CAmf0StreamIn _amf0sin;
        _amf0sin.DecodeAmf(_strInMessage.data(),_strInMessage.size());


        if (_amf0sin.At(0).toString() == "invite")
            return CRtmp::RTMP_DIAL;
        if (_amf0sin.At(0).toString() == "bye")
            return CRtmp::RTMP_HANG_OUT;

        return CRtmp::RTMP_COMMAND;
    }

    // incoming messae is some stream
    if (_RtmpMsgType == CRtmpMessage::AUDIO_MESSAGE ||
        _RtmpMsgType == CRtmpMessage::VIDEO_MESSAGE)
    {
        return CRtmp::RTMP_STREAM;
    }


    return CRtmp::RTMP_UNDEFINED;
}

bool CRtmp::ProcessMessage()
{
    cout << "ProcessMessage" << endl;

    string _strInMessage = m_RtmpMsg.GetPayload();


    if (m_RtmpMsg.GetMsgType() == CRtmpMessage::AMF0_COMMAND_MESSAGE)
    {
        CAmf0StreamIn _amf0sin;
         _amf0sin.DecodeAmf(_strInMessage.data(),_strInMessage.size());
        CMonitor::Draw(_amf0sin);


        if (_amf0sin.At(0).toString() == "createStream")
        {
            // send create stream reply ith correct transaction ID
            m_iTransID = int(_amf0sin.At(1).toDouble());
            m_vStreamsID.push_back(m_iTransID);
            m_RtmpProtocol.SendMessage(rtmpMsgGenerator.CreateStreamReply(m_iTransID));
            return true;
        }

        if (_amf0sin.At(0).toString() == "closeStream")
        {
            // send create stream reply ith correct transaction ID
            m_iTransID = int(_amf0sin.At(1).toDouble());
            m_vStreamsID.push_back(m_iTransID);
            m_RtmpProtocol.SendMessage(rtmpMsgGenerator.CreateStreamReply(m_iTransID));
            return true;
        }

        if (_amf0sin.At(0).toString() == "invite")
        {
            m_RtmpProtocol.SendMessage(rtmpMsgGenerator.InviteReply());
            return true;
        }

        if (_amf0sin.At(0).toString() == "publish")
        {
            m_iTransID = int(_amf0sin.At(1).toDouble());
            m_RtmpProtocol.SendMessage(rtmpMsgGenerator.PublishReply(m_iTransID),4);
            return true;
        }

        if (_amf0sin.At(0).toString() == "play")
        {
            m_iTransID = int(_amf0sin.At(1).toDouble());
            m_RtmpProtocol.SendMessage(rtmpMsgGenerator.PlayReply(m_iTransID),5);
            return true;
        }


    }

    return false;
}

bool CRtmp::CallAccepted()
{
    cout << endl << "Rtmp sends Call Acepted" << endl;
    m_RtmpProtocol.SendMessage(rtmpMsgGenerator.AcceptReply(m_vStreamsID.at(0)));
    return true;
}

bool CRtmp::SendBye()
{
    return m_RtmpProtocol.SendMessage(rtmpMsgGenerator.ByedRequest(m_iTransID));
}

bool CRtmp::SendRejected()
{
    return m_RtmpProtocol.SendMessage(rtmpMsgGenerator.RejectedRequest(m_iTransID));
}
